<template>
  <div class="app">
    <div class="header">
      <h1>Three.js 材质切换演示</h1>
      <p>使用右侧控制面板切换材质类型和调整属性</p>
    </div>
    <div ref="threeContainer" class="three-container"></div>
  </div>
</template>

<script>
import { ref, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'
import * as dat from 'dat.gui'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'
import { FileScanner } from './utils/fileScanner.js'

export default {
  name: 'App',
  setup() {
    const threeContainer = ref(null)
    let scene, camera, renderer, mesh, gui
    let animationId

    // 材质参数
    const materialParams = {
      materialType: 'MeshStandardMaterial',
      color: '#ffffff',
      metalness: 0.5,
      roughness: 0.5,
      wireframe: false,
      transparent: false,
      opacity: 1.0,
      emissive: '#000000',
      emissiveIntensity: 0.0,
      envMapIntensity: 1.0,
      // 纹理相关参数
      useTexture: false,
      textureType: 'none',
      textureRepeatU: 1,
      textureRepeatV: 1,
      textureOffsetU: 0,
      textureOffsetV: 0,
      normalScale: 1.0,
      bumpScale: 0.1,
      displacementScale: 0.1
    }

    // 几何体参数
    const geometryParams = {
      geometryType: 'BoxGeometry',
      rotationSpeed: 0.01,
      useCustomModel: false,
      modelFile: 'none'
    }

    // 光照参数
    const lightParams = {
      ambientIntensity: 0.4,
      directionalIntensity: 1.0,
      directionalX: 1,
      directionalY: 1,
      directionalZ: 1
    }

    let currentGeometry, currentMaterial
    let ambientLight, directionalLight
    let textureLoader, gltfLoader, objLoader, fbxLoader
    let loadedTextures = {}
    let loadedModels = {}
    let customModel = null
    let fileScanner = new FileScanner()
    let availableTextures = []
    let availableModels = []

    const init = async () => {
      // 创建场景
      scene = new THREE.Scene()
      scene.background = new THREE.Color(0x222222)

      // 创建相机
      camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      )
      camera.position.set(0, 0, 5)

      // 创建渲染器
      renderer = new THREE.WebGLRenderer({ antialias: true })
      renderer.setSize(window.innerWidth, window.innerHeight)
      renderer.shadowMap.enabled = true
      renderer.shadowMap.type = THREE.PCFSoftShadowMap
      threeContainer.value.appendChild(renderer.domElement)

      // 初始化加载器
      textureLoader = new THREE.TextureLoader()
      gltfLoader = new GLTFLoader()
      objLoader = new OBJLoader()
      fbxLoader = new FBXLoader()

      // 扫描可用的文件
      await scanAvailableFiles()

      // 创建光照
      setupLights()

      // 创建几何体和材质
      await createMesh()

      // 创建GUI
      createGUI()

      // 开始渲染循环
      animate()

      // 监听窗口大小变化
      window.addEventListener('resize', onWindowResize)
    }

    const setupLights = () => {
      // 环境光
      ambientLight = new THREE.AmbientLight(0x404040, lightParams.ambientIntensity)
      scene.add(ambientLight)

      // 方向光
      directionalLight = new THREE.DirectionalLight(0xffffff, lightParams.directionalIntensity)
      directionalLight.position.set(
        lightParams.directionalX,
        lightParams.directionalY,
        lightParams.directionalZ
      )
      directionalLight.castShadow = true
      directionalLight.shadow.mapSize.width = 2048
      directionalLight.shadow.mapSize.height = 2048
      scene.add(directionalLight)
    }

    const loadTexture = (path) => {
      return new Promise((resolve, reject) => {
        if (loadedTextures[path]) {
          resolve(loadedTextures[path])
          return
        }
        
        textureLoader.load(
          path,
          (texture) => {
            texture.wrapS = THREE.RepeatWrapping
            texture.wrapT = THREE.RepeatWrapping
            texture.repeat.set(materialParams.textureRepeatU, materialParams.textureRepeatV)
            texture.offset.set(materialParams.textureOffsetU, materialParams.textureOffsetV)
            loadedTextures[path] = texture
            resolve(texture)
          },
          undefined,
          (error) => {
            console.error('纹理加载失败:', error)
            reject(error)
          }
        )
      })
    }

    const loadModel = (path) => {
      return new Promise((resolve, reject) => {
        if (loadedModels[path]) {
          resolve(loadedModels[path].clone())
          return
        }

        const extension = path.split('.').pop().toLowerCase()
        
        const onLoad = (object) => {
          // 确保模型有正确的材质和几何体
          object.traverse((child) => {
            if (child.isMesh) {
              child.castShadow = true
              child.receiveShadow = true
            }
          })
          
          loadedModels[path] = object
          resolve(object.clone())
        }

        const onError = (error) => {
          console.error('模型加载失败:', error)
          reject(error)
        }

        switch (extension) {
          case 'gltf':
          case 'glb':
            gltfLoader.load(path, (gltf) => onLoad(gltf.scene), undefined, onError)
            break
          case 'obj':
            objLoader.load(path, onLoad, undefined, onError)
            break
          case 'fbx':
            fbxLoader.load(path, onLoad, undefined, onError)
            break
          default:
            reject(new Error('不支持的模型格式'))
        }
      })
    }

    const createGeometry = () => {
      switch (geometryParams.geometryType) {
        case 'BoxGeometry':
          currentGeometry = new THREE.BoxGeometry(2, 2, 2)
          break
        case 'SphereGeometry':
          currentGeometry = new THREE.SphereGeometry(1.5, 32, 32)
          break
        case 'CylinderGeometry':
          currentGeometry = new THREE.CylinderGeometry(1, 1, 2, 32)
          break
        case 'TorusGeometry':
          currentGeometry = new THREE.TorusGeometry(1, 0.4, 16, 100)
          break
        case 'ConeGeometry':
          currentGeometry = new THREE.ConeGeometry(1, 2, 32)
          break
        default:
          currentGeometry = new THREE.BoxGeometry(2, 2, 2)
      }
    }

    // 扫描可用文件
    const scanAvailableFiles = async () => {
      try {
        console.log('开始扫描可用文件...')
        
        // 并行扫描纹理和模型文件
        const [textureFiles, modelFiles] = await Promise.all([
          fileScanner.scanTextures('/textures'),
          fileScanner.scanModels('/models')
        ])
        
        // 更新可用文件列表
        availableTextures = textureFiles.map(file => file.name)
        availableModels = modelFiles.map(file => file.name)
        
        // 获取文件统计信息
        const textureStats = fileScanner.getFileStats(textureFiles)
        const modelStats = fileScanner.getFileStats(modelFiles)
        
        console.log('纹理文件扫描完成:', {
          files: availableTextures,
          stats: textureStats
        })
        console.log('模型文件扫描完成:', {
          files: availableModels,
          stats: modelStats
        })
        
        // 更新GUI选项
        updateGUIOptions()
      } catch (error) {
        console.warn('文件扫描失败:', error)
        // 使用默认文件列表作为后备
        availableTextures = ['checkerboard.svg', 'brick.svg', 'wood.svg', 'metal.svg', 'fabric.svg']
        availableModels = ['cube.gltf']
        updateGUIOptions()
      }
    }

    // 更新GUI选项
    const updateGUIOptions = () => {
      if (gui) {
        // 重新创建GUI以更新选项
        gui.destroy()
        createGUI()
      }
    }

    const createMaterial = async () => {
      const materialOptions = {
        color: materialParams.color,
        wireframe: materialParams.wireframe,
        transparent: materialParams.transparent,
        opacity: materialParams.opacity
      }
      if (materialParams.useTexture && materialParams.textureType !== 'none') {
        try {
          const texturePath = `/textures/${materialParams.textureType}.svg`
          const texture = await loadTexture(texturePath)
          texture.repeat.set(materialParams.textureRepeatU, materialParams.textureRepeatV)
          texture.offset.set(materialParams.textureOffsetU, materialParams.textureOffsetV)
          materialOptions.map = texture
        } catch (error) {
          console.warn('纹理加载失败，使用默认材质:', error)
        }
      }

      switch (materialParams.materialType) {
        case 'MeshBasicMaterial':
          currentMaterial = new THREE.MeshBasicMaterial(materialOptions)
          break
        case 'MeshLambertMaterial':
          currentMaterial = new THREE.MeshLambertMaterial({
            ...materialOptions,
            emissive: materialParams.emissive,
            emissiveIntensity: materialParams.emissiveIntensity
          })
          break
        case 'MeshPhongMaterial':
          currentMaterial = new THREE.MeshPhongMaterial({
            ...materialOptions,
            emissive: materialParams.emissive,
            emissiveIntensity: materialParams.emissiveIntensity,
            shininess: 100
          })
          break
        case 'MeshStandardMaterial':
          currentMaterial = new THREE.MeshStandardMaterial({
            ...materialOptions,
            metalness: materialParams.metalness,
            roughness: materialParams.roughness,
            emissive: materialParams.emissive,
            emissiveIntensity: materialParams.emissiveIntensity,
            envMapIntensity: materialParams.envMapIntensity
          })
          break
        case 'MeshPhysicalMaterial':
          currentMaterial = new THREE.MeshPhysicalMaterial({
            ...materialOptions,
            metalness: materialParams.metalness,
            roughness: materialParams.roughness,
            emissive: materialParams.emissive,
            emissiveIntensity: materialParams.emissiveIntensity,
            envMapIntensity: materialParams.envMapIntensity,
            clearcoat: 0.3,
            clearcoatRoughness: 0.25
          })
          break
        default:
          currentMaterial = new THREE.MeshStandardMaterial(materialOptions)
      }
    }

    const createMesh = async () => {
      if (mesh) {
        scene.remove(mesh)
        if (currentGeometry) currentGeometry.dispose()
        if (currentMaterial) currentMaterial.dispose()
      }

      // 检查是否使用自定义模型
      if (geometryParams.useCustomModel && geometryParams.modelFile !== 'none') {
        try {
          const modelPath = `/models/${geometryParams.modelFile}`
          customModel = await loadModel(modelPath)
          
          // 为自定义模型应用材质
          await createMaterial()
          customModel.traverse((child) => {
            if (child.isMesh) {
              child.material = currentMaterial
              child.castShadow = true
              child.receiveShadow = true
            }
          })
          
          mesh = customModel
          scene.add(mesh)
          return
        } catch (error) {
          console.warn('模型加载失败，使用默认几何体:', error)
          geometryParams.useCustomModel = false
        }
      }

      // 使用默认几何体
      createGeometry()
      await createMaterial()
      mesh = new THREE.Mesh(currentGeometry, currentMaterial)
      mesh.castShadow = true
      mesh.receiveShadow = true
      scene.add(mesh)
    }

    const updateMaterial = async () => {
      await createMaterial()
      if (mesh) {
        if (geometryParams.useCustomModel) {
          mesh.traverse((child) => {
            if (child.isMesh) {
              child.material = currentMaterial
            }
          })
        } else {
          mesh.material = currentMaterial
        }
      }
    }

    const updateGeometry = async () => {
      if (geometryParams.useCustomModel) {
        await createMesh()
      } else {
        createGeometry()
        if (mesh) {
          mesh.geometry.dispose()
          mesh.geometry = currentGeometry
        }
      }
    }

    const updateTexture = async () => {
      if (materialParams.useTexture && materialParams.textureType !== 'none') {
        try {
          const texturePath = `/textures/${materialParams.textureType}.svg`
          const texture = await loadTexture(texturePath)
          texture.repeat.set(materialParams.textureRepeatU, materialParams.textureRepeatV)
          texture.offset.set(materialParams.textureOffsetU, materialParams.textureOffsetV)
          
          if (currentMaterial) {
            currentMaterial.map = texture
            currentMaterial.needsUpdate = true
          }
        } catch (error) {
          console.warn('纹理更新失败:', error)
        }
      } else if (currentMaterial) {
        currentMaterial.map = null
        currentMaterial.needsUpdate = true
      }
    }

    const updateLights = () => {
      ambientLight.intensity = lightParams.ambientIntensity
      directionalLight.intensity = lightParams.directionalIntensity
      directionalLight.position.set(
        lightParams.directionalX,
        lightParams.directionalY,
        lightParams.directionalZ
      )
    }

    const createGUI = () => {
      gui = new dat.GUI({ width: 320 })

      // 材质类型文件夹
      const materialFolder = gui.addFolder('材质设置')
      materialFolder.add(materialParams, 'materialType', [
        'MeshBasicMaterial',
        'MeshLambertMaterial',
        'MeshPhongMaterial',
        'MeshStandardMaterial',
        'MeshPhysicalMaterial'
      ]).name('材质类型').onChange(updateMaterial)

      materialFolder.addColor(materialParams, 'color').name('颜色').onChange(updateMaterial)
      materialFolder.add(materialParams, 'wireframe').name('线框模式').onChange(updateMaterial)
      materialFolder.add(materialParams, 'transparent').name('透明').onChange(updateMaterial)
      materialFolder.add(materialParams, 'opacity', 0, 1, 0.01).name('不透明度').onChange(updateMaterial)

      // 纹理设置文件夹
      const textureFolder = materialFolder.addFolder('纹理设置')
      textureFolder.add(materialParams, 'useTexture').name('使用纹理').onChange(updateTexture)
      
      // 动态创建纹理类型选项
      const textureOptions = ['none', ...availableTextures.map(tex => tex.replace('.svg', ''))]
      textureFolder.add(materialParams, 'textureType', textureOptions).name('纹理类型').onChange(updateTexture)
      
      textureFolder.add(materialParams, 'textureRepeatU', 0.1, 10, 0.1).name('U重复').onChange(updateTexture)
      textureFolder.add(materialParams, 'textureRepeatV', 0.1, 10, 0.1).name('V重复').onChange(updateTexture)
      textureFolder.add(materialParams, 'textureOffsetU', -1, 1, 0.01).name('U偏移').onChange(updateTexture)
      textureFolder.add(materialParams, 'textureOffsetV', -1, 1, 0.01).name('V偏移').onChange(updateTexture)

      // PBR材质属性
      const pbrFolder = materialFolder.addFolder('PBR属性')
      pbrFolder.add(materialParams, 'metalness', 0, 1, 0.01).name('金属度').onChange(updateMaterial)
      pbrFolder.add(materialParams, 'roughness', 0, 1, 0.01).name('粗糙度').onChange(updateMaterial)
      pbrFolder.addColor(materialParams, 'emissive').name('自发光颜色').onChange(updateMaterial)
      pbrFolder.add(materialParams, 'emissiveIntensity', 0, 2, 0.01).name('自发光强度').onChange(updateMaterial)
      pbrFolder.add(materialParams, 'envMapIntensity', 0, 3, 0.01).name('环境贴图强度').onChange(updateMaterial)

      materialFolder.open()

      // 几何体设置
      const geometryFolder = gui.addFolder('几何体设置')
      geometryFolder.add(geometryParams, 'useCustomModel').name('使用自定义模型').onChange(updateGeometry)
      
      // 动态创建模型文件选项
      const modelOptions = ['none', ...availableModels]
      geometryFolder.add(geometryParams, 'modelFile', modelOptions).name('模型文件').onChange(updateGeometry)
      geometryFolder.add(geometryParams, 'geometryType', [
        'BoxGeometry',
        'SphereGeometry',
        'CylinderGeometry',
        'TorusGeometry',
        'ConeGeometry'
      ]).name('几何体类型').onChange(updateGeometry)
      geometryFolder.add(geometryParams, 'rotationSpeed', 0, 0.05, 0.001).name('旋转速度')
      geometryFolder.open()

      // 光照设置
      const lightFolder = gui.addFolder('光照设置')
      lightFolder.add(lightParams, 'ambientIntensity', 0, 2, 0.01).name('环境光强度').onChange(updateLights)
      lightFolder.add(lightParams, 'directionalIntensity', 0, 3, 0.01).name('方向光强度').onChange(updateLights)
      lightFolder.add(lightParams, 'directionalX', -5, 5, 0.1).name('方向光X').onChange(updateLights)
      lightFolder.add(lightParams, 'directionalY', -5, 5, 0.1).name('方向光Y').onChange(updateLights)
      lightFolder.add(lightParams, 'directionalZ', -5, 5, 0.1).name('方向光Z').onChange(updateLights)
      lightFolder.open()

      // 文件上传控制
      const fileFolder = gui.addFolder('文件管理')
      
      // 添加文件过滤选项
      const filterOptions = {
        textureFilter: 'all',
        modelFilter: 'all'
      }
      
      const textureFilterFolder = fileFolder.addFolder('纹理过滤')
      textureFilterFolder.add(filterOptions, 'textureFilter', ['all', 'svg', 'raster']).name('纹理类型').onChange((value) => {
        console.log('纹理过滤:', value)
        // 这里可以添加过滤逻辑
      })
      
      const modelFilterFolder = fileFolder.addFolder('模型过滤')
      modelFilterFolder.add(filterOptions, 'modelFilter', ['all', 'gltf', 'obj', 'fbx']).name('模型类型').onChange((value) => {
        console.log('模型过滤:', value)
        // 这里可以添加过滤逻辑
      })
      
      // 添加刷新按钮
      const refreshFiles = {
        refresh: async () => {
          console.log('强制重新扫描所有文件...')
          const result = await fileScanner.forceRescan()
          availableTextures = result.textures.map(file => file.name)
          availableModels = result.models.map(file => file.name)
          updateGUIOptions()
        }
      }
      fileFolder.add(refreshFiles, 'refresh').name('刷新文件列表')
      
      const fileControls = {
        uploadTexture: () => {
          const input = document.createElement('input')
          input.type = 'file'
          input.accept = '.jpg,.jpeg,.png,.svg,.webp'
          input.onchange = async (e) => {
            const file = e.target.files[0]
            if (file) {
              try {
                // 这里可以添加文件上传到服务器的逻辑
                // 目前只是添加到已知文件列表中
                fileScanner.addKnownTexture(file.name)
                await scanAvailableFiles()
                console.log('纹理文件已添加:', file.name)
                
                const reader = new FileReader()
                reader.onload = (event) => {
                  const texture = textureLoader.load(event.target.result)
                  texture.wrapS = THREE.RepeatWrapping
                  texture.wrapT = THREE.RepeatWrapping
                  if (currentMaterial) {
                    currentMaterial.map = texture
                    currentMaterial.needsUpdate = true
                  }
                }
                reader.readAsDataURL(file)
              } catch (error) {
                console.error('上传纹理失败:', error)
              }
            }
          }
          input.click()
        },
        uploadModel: () => {
          const input = document.createElement('input')
          input.type = 'file'
          input.accept = '.gltf,.glb,.obj,.fbx'
          input.onchange = async (e) => {
            const file = e.target.files[0]
            if (file) {
              try {
                // 这里可以添加文件上传到服务器的逻辑
                // 目前只是添加到已知文件列表中
                fileScanner.addKnownModel(file.name)
                await scanAvailableFiles()
                console.log('模型文件已添加:', file.name)
                
                const url = URL.createObjectURL(file)
                const model = await loadModel(url)
                if (mesh) {
                  scene.remove(mesh)
                }
                mesh = model
                scene.add(mesh)
                geometryParams.useCustomModel = true
              } catch (error) {
                console.error('模型上传失败:', error)
                alert('模型上传失败，请检查文件格式')
              }
            }
          }
          input.click()
        }
      }
      fileFolder.add(fileControls, 'uploadTexture').name('上传纹理')
      fileFolder.add(fileControls, 'uploadModel').name('上传模型')
      fileFolder.open()
    }

    const animate = () => {
      animationId = requestAnimationFrame(animate)

      // 旋转模型
      if (mesh) {
        mesh.rotation.x += geometryParams.rotationSpeed
        mesh.rotation.y += geometryParams.rotationSpeed
      }

      renderer.render(scene, camera)
    }

    const onWindowResize = () => {
      camera.aspect = window.innerWidth / window.innerHeight
      camera.updateProjectionMatrix()
      renderer.setSize(window.innerWidth, window.innerHeight)
    }

    onMounted(async () => {
      await init()
    })

    onUnmounted(() => {
      if (animationId) {
        cancelAnimationFrame(animationId)
      }
      if (gui) {
        gui.destroy()
      }
      if (renderer) {
        renderer.dispose()
      }
      window.removeEventListener('resize', onWindowResize)
    })

    return {
      threeContainer
    }
  }
}
</script>

<style scoped>
.app {
  position: relative;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

.header {
  position: absolute;
  top: 20px;
  left: 20px;
  z-index: 100;
  color: white;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}

.header h1 {
  font-size: 2rem;
  margin-bottom: 0.5rem;
  font-weight: 300;
}

.header p {
  font-size: 1rem;
  opacity: 0.9;
}

.three-container {
  width: 100%;
  height: 100%;
}

/* 自定义GUI样式 */
:global(.dg.ac) {
  z-index: 1000 !important;
}

:global(.dg.main) {
  position: fixed !important;
  top: 20px !important;
  right: 20px !important;
}

:global(.dg .close-button) {
  background: #2c3e50 !important;
}

:global(.dg .cr.function) {
  background: #34495e !important;
  border-color: #2c3e50 !important;
}

:global(.dg .cr.function:hover) {
  background: #3498db !important;
}

:global(.dg li:not(.folder)) {
  background: rgba(0, 0, 0, 0.1) !important;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
}

:global(.dg li.folder) {
  background: rgba(0, 0, 0, 0.2) !important;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important;
}
</style>